#include "DrawLines.h"

int DrawLines::WINDOW_WIDTH = 320;
int DrawLines::WINDOW_HEIGHT = 240;

DrawLines::DrawLines(void)
{
	/* Initialize the SDL library */
    if (SDL_Init(SDL_INIT_VIDEO) < 0)
	{
        cerr << "Couldn't initialize SDL: " << SDL_GetError() << endl;
    }

	const char* WINDOW_TITLE = "Sample Draw lines";

	/* Initialize the display, requesting a software surface */
    screen = SDL_SetVideoMode(WINDOW_WIDTH, WINDOW_HEIGHT, 0, SDL_HWSURFACE | SDL_DOUBLEBUF);
    if (screen == NULL)
	{
        cerr << "Couldn't set " << WINDOW_WIDTH << "x" << WINDOW_HEIGHT << " video mode: " << SDL_GetError() << endl;
    }
	else
	{
		SDL_WM_SetCaption(WINDOW_TITLE, 0);
	}
}

DrawLines::~DrawLines(void)
{
	SDL_FreeSurface(screen);
	SDL_Quit();
}

void DrawLines::run(void)
{
	bool quit = false;
	SDL_Event event;
	Uint32 yellow = SDL_MapRGB(screen->format, 0xff, 0xff, 0x00);

	SDL_LockSurface(screen);
	drawLine(35, 35, 125, 125, yellow);
	drawLine(70, 180, 70, 40, yellow);
	SDL_UnlockSurface(screen);
	SDL_UpdateRect(screen, 0, 0, 0, 0);

	while (!quit)
	{
		while (SDL_PollEvent(&event))
		{
			switch (event.type)
			{
				case SDL_KEYDOWN:
					{
						if (event.key.keysym.sym == SDLK_ESCAPE)
						{
							quit = true;
						}
					}
				case SDL_QUIT:
					quit = true;
					break;
			}
		}
		SDL_PumpEvents();
	}
}

/*
 * The method of rendering the line is to loop through the points in one dimension (x or y) and calculate the corresponding position in the other dimension,
 * using the line's slope. If there are more points in the x dimension (abs(xdiff)>abs(ydiff)), it loops through the points in the x dimension and calculate
 * the corresponding y values; otherwise, it loops through the points in the y dimension and calculate the corresponding x values.
 * Handling these two cases separately is necessary to prevent gaps in the line.
 */
void DrawLines::drawLine(int x1, int y1, int x2, int y2, Uint32 color)
{
	int xdiff = x2-x1;
	int ydiff = y2-y1;

	if (absoluteValue(xdiff) > absoluteValue(ydiff))
	{
		int xmin, xmax;
		if (x1 < x2)
		{
			xmin = x1;
            xmax = x2;
		}
		else
		{
			xmin = x2;
            xmax = x1;
		}
		float slope = ydiff / xdiff;
		float y = 0.0;
		for (float x = xmin; x <= xmax; x += 1.0f)
		{
			y = y1 + ((x - x1) * slope);
			putPixel(round(x), round(y), color);
		}
	}
	else
	{
		int ymin, ymax;
		if (y1 < y2)
		{
			ymin = y1;
            ymax = y2;
		}
		else
		{
			ymin = y2;
            ymax = y1;
		}
		float slope = xdiff / ydiff;
		float x = 0.0;
		for (float y = ymin; y <= ymax; y += 1.0f)
		{
			x = x1 + ((y - y1) * slope);
			putPixel(round(x), round(y), color);
		}
	}
}

int DrawLines::absoluteValue(int value)
{
	int newValue;
	if (value >= 0)
	{
		newValue = value;
	}
	else
	{
		newValue = -value;
	}
	return newValue;
}

int DrawLines::round(float value)
{
	return (int)(value+0.5);
}

/*
 * Set the pixel at (x, y) to the given value
 * NOTE: The surface must be locked before calling this!
 */
void DrawLines::putPixel(int x, int y, Uint32 pixel)
{
    int bpp = screen->format->BytesPerPixel;
    /* Here p is the address to the pixel we want to set */
    Uint8 *p = (Uint8*)screen->pixels + y * screen->pitch + x * bpp;

    switch(bpp)
	{
	case 1:
        *p = pixel;
        break;
    case 2:
        *(Uint16*)p = pixel;
        break;

    case 3:
        if (SDL_BYTEORDER == SDL_BIG_ENDIAN)
		{
            p[0] = (pixel >> 16) & 0xff;
            p[1] = (pixel >> 8) & 0xff;
            p[2] = pixel & 0xff;
        }
		else
		{
            p[0] = pixel & 0xff;
            p[1] = (pixel >> 8) & 0xff;
            p[2] = (pixel >> 16) & 0xff;
        }
        break;
    case 4:
        *(Uint32*)p = pixel;
        break;
    }
}
